# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time    : 2022/9/1
# @Author  : moe@miku39.fun
# @File    : Coro.py
# @Software: PyCharm
import os
import re
import shutil
import time

import requests

from . import __banner__
from .service.impl import Video, AnimeFilm
from .util import FFmpeg, Toml, Cookies, ParserURL

__all__ = ["Bili"]

# 工作目录
WORK_PATH = os.getcwd()
# 配置文件路径
CONFIG_PATH = os.path.join(WORK_PATH, "config.toml")
# 文件临时输出路径
TEMP_PATH = os.path.join(WORK_PATH, "temp")
# 视频持久化处理存放路径
OUTPUT_PATH = os.path.join(WORK_PATH, "output")


def setFFmpeg():
    # 如果环境变量存在FFmpeg，就没必要自定义存放路径了
    if FFmpeg.FFmpegOperation.exist():
        return ""
    ffmpeg_path = input("请输入FFmpeg存放路径(允许后续修改):")
    if ffmpeg_path:
        return ffmpeg_path
    else:
        return ""


def setOutputPath():
    outputPath = input("请输入视频文件存放路径(default: workDir):")
    if outputPath:
        return outputPath
    else:
        return OUTPUT_PATH


def keepTempFiles():
    while True:
        if choice := input("视频合并后，是否保留临时文件[Y/N](default: N):"):
            if re.match("y", choice, re.I):
                return True
            elif re.match("n", choice, re.I):
                return False
            else:
                print("输入有误 Σ(ﾟдﾟ;)")
        else:
            return False


def setBlockSize():
    print("分块下载的块大小。单位:MiB。可选参数[非零整数、回车(使用默认值)、数值0(禁用分块下载)]")
    while True:
        if choice := input("请输入块大小(default: 128):"):
            if int(choice) == 0:
                return 0
            elif int(choice) > 0:
                return int(choice)
            else:
                print("输入有误 Σ(ﾟдﾟ;)")
        else:
            return 128


def setStream():
    while True:
        if choice := input("是否启用流下载[Y/N](default: Y):"):
            if re.match("y", choice, re.I):
                return True
            elif re.match("n", choice, re.I):
                return False
            else:
                print("输入有误 Σ(ﾟдﾟ;)")
        else:
            return True


def setProcess():
    while True:
        if choice := input("是否启用多进程[Y/N](default: N):"):
            if re.match("y", choice, re.I):
                return True
            elif re.match("n", choice, re.I):
                return False
            else:
                print("输入有误 Σ(ﾟдﾟ;)")
        else:
            return False


def setWorkerNum():
    print(f"同时下载的视频个数。可选参数[非零整数、回车(使用默认值)]")
    while True:
        if choice := input("请输入数值(default: 4):"):
            if int(choice) > 0:
                return int(choice)
            else:
                print("输入有误 Σ(ﾟдﾟ;)")
        else:
            return 4


# 配置文件初始化
if not os.path.exists(CONFIG_PATH):
    print("配置文件不存在，需进行预配置设定!\n如需修改 以文本方式打开配置文件[config.toml]\n")
    ffmpegPath = setFFmpeg()
    OUTPUT_PATH = setOutputPath()
    keepChoice = keepTempFiles()
    blockSize = setBlockSize()
    stream = setStream()
    multiprocess = setProcess()
    workerNum = setWorkerNum()
    toml = Toml.TomlOperation(CONFIG_PATH)
    toml.updateToml(
        {
            "option": {
                "ffmpegPath": ffmpegPath,
                "outputPath": OUTPUT_PATH,
                "tempPath": TEMP_PATH,
                "keepTempFiles": keepChoice,
                "blockSize": blockSize,
                "stream": stream,
                "multiprocess": multiprocess,
                "workerNum": workerNum,
            },
            "account": {
                "DedeUserID": "",
                "DedeUserID__ckMd5": "",
                "SESSDATA": "",
                "bili_jct": "",
                "sid": "",
            },
        }
    )


class Bili:
    def __init__(self) -> None:
        self.videoSourceList = None
        self.ffmpegEXEC = None
        self.videoInfo = None
        self.config = {}
        self.banner = __banner__
        self.system = os.name
        self.sess = requests.session()
        self.configFile = CONFIG_PATH
        self.toml = Toml.TomlOperation(self.configFile)
        self.biliCookies = Cookies.GetCookies(self.sess, self.system)
        self.parserOfUrl = ParserURL.URLOperation(self.sess)
        self.ffmpeg = FFmpeg.FFmpegOperation
        self.spider = None
        self.sess.headers.update(
            {
                "origin": "https://www.bilibili.com",
                "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                              "Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.67",
            }
        )

    @classmethod
    def init_dir(cls, tmp, out):
        if not os.path.exists(tmp):
            os.makedirs(tmp)
        if not os.path.exists(out):
            os.makedirs(out)

    def loginByQR(self) -> None:
        self.sess.cookies.clear()
        qrcodeURL, oauthKey = self.biliCookies.getLoginUrl()
        self.biliCookies.make_QR_code(qrcodeURL)
        cookies = self.biliCookies.login(oauthKey)
        for key, value in cookies.items():
            self.config["account"][key] = value
        self.toml.updateToml(self.config)

    def loginByLocal(self) -> None:
        for key, value in self.config["account"].items():
            self.sess.cookies.set(key, value)

    def callLogin(self) -> None:
        if all([value for value in self.config["account"].values()]):
            self.loginByLocal()
        else:
            self.loginByQR()
        self.warpOption()

    def warpOption(self):
        self.config["option"]["blockSize"] = (
            self.config["option"]["blockSize"] * 1024 * 1024
        )

    def setReferer(self, video_base_info) -> None:
        if video_base_info["idType"] == "season_id":
            self.sess.headers.update(
                {
                    "referer": f"https://www.bilibili.com/bangumi/play/ss{video_base_info['videoID']}"
                }
            )
        elif video_base_info["idType"] == "ep_id":
            self.sess.headers.update(
                {
                    "referer": f"https://www.bilibili.com/bangumi/play/ep{video_base_info['videoID']}"
                }
            )
        else:
            self.sess.headers.update(
                {
                    "referer": f"https://www.bilibili.com/video/{video_base_info['videoID']}"
                }
            )

    def tryLogin(self) -> bool:
        while True:
            if result := input("[1.登录本地账号\t2.扫码登录\t3.取消登录(default)]:"):
                if result.isdigit() and result in ["1", "2", "3"]:
                    if result == "1":
                        self.loginByLocal()
                        if self.getVipStatus() is True:
                            return True
                        print("本地账号不存在或非大会员账号 Σ_(꒪ཀ꒪」∠)_")
                    elif result == "2":
                        self.loginByQR()
                        if self.getVipStatus() is True:
                            return True
                        print("非大会员账号 (º﹃º )")
                    elif result == "3":
                        return False
            else:
                return False

    def getVipStatus(self) -> bool:
        url = "https://api.bilibili.com/x/web-interface/nav"
        response = self.sess.get(url=url).json()
        if response["code"] == -101 and response["data"]["isLogin"] is False:
            return False
        elif response["code"] == 0 and response["data"]["isLogin"] is True:
            if response["data"]["vipStatus"] == 0:  # 未开通大会员
                return False
            elif response["data"]["vipStatus"] == 1:  # 已开通大会员
                return True

    def parserVideo(self) -> bool:
        while True:
            text = input("请输入视频号或视频网址:")
            if videoBaseInfo := self.parserOfUrl.match(text):
                if videoBaseInfo["videoType"] == "video":
                    self.setReferer(videoBaseInfo)
                    video = Video.Video(self.sess)
                    break
                elif videoBaseInfo["videoType"] == "animeFilm":
                    if self.getVipStatus() is False:
                        print("根据您提交的视频号，判断该视频是番剧，部分番剧需提供大会员账号 （￣へ￣）")
                        if self.tryLogin() is False:
                            return False
                    self.setReferer(videoBaseInfo)
                    video = AnimeFilm.AnimeFilm(self.sess)
                    break
            else:
                print("视频地址有误 或 视频不存在 或 该视频仅支持海外网络 (-_-#)")
        if videoInfo := video.getVideoInfo(videoBaseInfo):
            self.videoInfo = videoInfo
            self.videoSourceList = video.getVideoPlayUrl(videoBaseInfo, self.videoInfo)
            return True
        else:
            print("视频不存在或该视频仅支持海外环境 (-_-#)")
            return False

    def runners(self, login) -> None:
        """建议将`login`设为`True`,以致获取更高画质的视频资源"""
        print(self.banner)
        Bili.init_dir(TEMP_PATH, OUTPUT_PATH)
        self.config = self.toml.readToml()
        if ffmpegEXEC := self.ffmpeg.existFFmpeg(self.config["option"]["ffmpegPath"]):
            self.ffmpegEXEC = ffmpegEXEC
        else:
            print("检测到未配置FFmpeg (´･_･`)")
            exit(1)
        if login is True:
            self.callLogin()
        if self.parserVideo() is True:
            if self.config["option"]["multiprocess"]:
                from .download import MultiprocessSpider

                self.spider = MultiprocessSpider.Spider(self.sess, self.system)
            else:
                from .download import AsyncSpider

                self.spider = AsyncSpider.Spider(self.sess, self.system)
            videoBlockList = self.spider.download(self.videoInfo, self.videoSourceList, self.config["option"],
                                                  self.banner)
            for each in videoBlockList:
                self.ffmpeg.merge(self.ffmpegEXEC, each["videoPath"], each["audioPath"], each["FinalPath"])
            time.sleep(1)
            print("\n视频已下载全部完成，快去看看吧 (๑>؂<๑）\n")
        if not self.config["option"]["keepTempFiles"]:
            try:
                if os.path.exists("qrcode.png"):
                    os.remove("qrcode.png")
                shutil.rmtree(TEMP_PATH)
            except Exception as error:
                print(f"删除临时视频文件失败，失败原因: {error}")
